/* Copyright 2014 Jan Wolter */

#include "stack.h"
#include "tableau.h"
#include "found.h"

/* Array of tableau piles used in this game */
TableauStack *tableau;
int tableausize= 0;	/* current size of tableau array */

/* Does card c2 fit on top of card c1 for moving from tableau column t? */
int movefit(TableauStack *t, Card *c1, Card *c2)
{
    return (c1->faceup && suitmatch(t->movesuit, c1->s, c2->s) &&
	    rankmatch(t->movestep, t->basecard, c1->n, c2->n));
}

/* Does card c2 fit on top of card c1 for building on tableau column t? */
int buildfit(TableauStack *t, Card *c1, Card *c2)
{
    return (suitmatch(t->buildsuit, c1->s, c2->s) &&
	    rankmatch(t->buildstep, t->basecard, c1->n, c2->n));
}

int tableau_mayInsert(Stack *dst, Card *card, int len, Stack *src)
{
    int next;
    Stack *nextstack;
    TableauStack *t= tableau + dst->tid;
    Card *topdst= topStack(dst);

    if (dst->n == 0)
    {
	/* Empty stack - only the leftmost empty stack may be played into */
	/* We do this to reduce the number of possible moves */
	for (int i= t->beginset; i < dst->tid; i++)
	    if (stacks[tableau[i].id].n == 0) return 0;

	/* Don't allow the move if it would empty another tableau stack. This
	 * prevents infinite loops of moving kings back and forth between
	 * empty stacks.
	 */
	if (src->type == TABLEAU && len == src->n &&
	    (t->fillblank != F_KINGLEFT || src->tid != dst->tid+1) &&
	    tableau[src->tid].fillblank != S_NONE)
	    return 0;

	/* otherwise fillblank determine what we can do */
	switch (t->fillblank)
	{
	    case F_ANYTHING:
		return 1;
	    case F_KINGONLY:
		return (card->n == 13);
	    case F_KINGLEFT:
		next= dst->tid+1;
		/* If the next tableau column is empty or non-existent, then
		 * any king may be moved in */
		if (next == ntableau ||
		    (nextstack= stacks+tableau[next].id)->n == 0)
		    return (card->n == 13);
		/* Otherwise only moves from that next column are allowed */
		if (nextstack != src) return 0;
		/* And they must be moves of maximal movable stacks */
		int i= src->n - len - 1;
		return (i < 0 || !movefit(tableau+next, src->c[i], card));
	    case F_NONE:
	    default:
		return 0;
	}
    }
    else if (src->type == TABLEAU && ndeck > 1 && src->n > len)
    {
	/* Don't move the JH back and fourth between two QH cards either */
	Card *topsrc= src->c[src->n - len - 1];
	if (topsrc->n == topdst->n && topsrc->s == topdst->s)
	    return 0;
    }

    return (suitmatch(t->buildsuit, card->s, topdst->s) &&
	    rankmatch(t->buildstep, t->basecard, topdst->n, card->n));
}

/* After cards have been removed, make sure the the top card is face up.
 * Autofills would probably go here too.
 */
/*
void tableau_postMove(Stack *stack)
{
    Card *c= topStack(stack);
    if (c) c->faceup= 1;
}
*/

int tableau_firstMove(Stack *stack, int *index1, int *len)
{
    if (stack->n == 0) return 0;

    /* This is our normal first move - just removing the top card
     * Even in games where we don't break sequences, we will still want to
     * try that so we can move single cards to the foundation.
     */
    *index1= stack->n-1;
    *len= 1;

    /* But in rare circumstance we won't want to do that. Like when we are only
     * moving complete sequences to the foundation, and there is more than one
     * faceup card on the stack. */
    if (superfound == sf_complete && stack->n > 1 && stack->c[stack->n-2]->faceup)
    {
	/* We'll still want to propose the single card move if we have
	 * BREAK_ANYWHERE or we have BREAK_NOFIT and the top card doesn't
	 * fit on the second. */
	TableauStack *t= tableau + stack->tid;
	if (t->nobreak == BREAK_NOWHERE ||
	    (t->nobreak == BREAK_NOFIT &&
	     stack->c[stack->n-1]->n != 13 &&
	     buildfit(t, stack->c[stack->n-2],stack->c[stack->n-1])))
	    return tableau_nextMove(stack, index1, len);
    }

    /* Just return the single card move */
    return 1;
}

int tableau_nextMove(Stack *stack, int *index1, int *len)
{
    TableauStack *t= tableau + stack->tid;

    if (*len == 1)
    {
	/* Doing the second move - find the longest possible movable sequence */
	int i;
	for (i= *index1 - 1; i >= 0; i--)
	{
	    if (!movefit(t, stack->c[i], stack->c[i+1]))
		break;
	}
	/* i is now the index of the topmost unmovable card */
	if (i == *index1 - 1) return 0;
	*index1= i+1;
	*len= stack->n - *index1;
    }
    else if (t->nobreak == BREAK_NOWHERE || *len == 2)
	/* No shorter sequences possible */
	return 0;
    else
    {
	/* Try a slightly smaller stack */
	do {
	    (*index1)++;
	    (*len)--;
	    if (*len < 2) return 0;
	} while (t->nobreak == BREAK_ANYWHERE ||
	    (stack->c[*index1]->n != 13 &&
	     buildfit(t, stack->c[*index1-1], stack->c[*index1])));
	return 1;
    }
}

int isfaceup(int dealflip, int i)
{
    switch (dealflip)
    {
	case FLIP_NONE: return 1;
	case FLIP_ALL: return 0;
	case FLIP_ONE: return (i > 0);
	case FLIP_TWO: return (i > 1);
	case FLIP_THREE: return (i > 2);
	case FLIP_FOUR: return (i > 3);
    }
}

void makeTableau(int ndeal, int dealflip, Stack *deal,
    int buildsuit, int buildstep, int movesuit, int movestep,
    int basecard, int fillblank, int nobreak, int beginset)
{
    /* Make sure we have memory for another tableau pile */
    if (tableausize == 0)
    {
	tableausize= 8;
	tableau= (TableauStack *)malloc(tableausize*sizeof(TableauStack));
    }
    else if (ntableau == tableausize)
    {
	tableausize+= 8;
	tableau= (TableauStack *)realloc(tableau,tableausize*sizeof(TableauStack));
    }

    /* Create the tableau pile */
    tableau[ntableau].buildsuit= buildsuit;
    tableau[ntableau].buildstep= buildstep;
    tableau[ntableau].movesuit= movesuit;
    tableau[ntableau].movestep= movestep;
    tableau[ntableau].fillblank= fillblank;
    tableau[ntableau].nobreak= nobreak;
    tableau[ntableau].beginset= beginset;
    tableau[ntableau].basecard= basecard;

    /* It starts out with ncards, but if we are allowing building we increase
     * that. */
    int size= ndeal;
    if (buildsuit != S_NONE)
    {
	size*= 2;
	if (size < npip) size= npip;
	if (size > ncard) size= ncard;
    }

    int i= tableau[ntableau].id= newStack(TABLEAU,ntableau,size,
	    (buildsuit == S_NONE) ? NULL : tableau_mayInsert,
	    tableau_firstMove, tableau_nextMove,
	    NULL);

    /* Deal in the cards */
    for (int j= 0; j < ndeal; j++)
    {
	Card *c= popStack(deal);
	if (c == NULL) break;
	c->faceup= (j == ndeal-1 || isfaceup(dealflip, j));
	pushStack(stacks+i, c);
    }
    ntableau++;
}


void cleanTableau()
{
    free(tableau);
    ntableau= tableausize= 0;
}

void printTableau(FILE *f, Stack *stks)
{
    char label[8];
    for (int k= 0; k < ntableau; k++)
    {
	sprintf(label,"T%02d", k+1);
	printStack(f, label, stks+tableau[k].id);
    }
}


/* Append all tableau cards to the given stack, first the face up ones, in
 * reverse order, then the facedown ones, in the original order. The tableau
 * stacks are not altered.
 */
void gatherTableau(Stack *s)
{
    /* Gather face up cards on top of each stack, reversing them */
    for (int k= 0; k < ntableau; k++)
    {
	Stack *fs= stacks+tableau[k].id;
	for (int i= fs->n-1; i >= 0; i--)
	{
	    Card *c= fs->c[i];
	    if (c->faceup)
		pushStack(s, c);
	}
    }

    /* Now gather up all the face down cards, not reversing them. */
    for (int k= 0; k < ntableau; k++)
    {
	Stack *fs= stacks+tableau[k].id;
	for (int i= 0; i < fs->n; i++)
	{
	    Card *c= fs->c[i];
	    if (!c->faceup)
		pushStack(s, c);
	}
    }
}
